<!DOCTYPE html>
<html>
<head>
<title>SeaskyProtocol.md</title>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">

<style>
/* https://github.com/microsoft/vscode/blob/master/extensions/markdown-language-features/media/markdown.css */
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

body {
	font-family: var(--vscode-markdown-font-family, -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif);
	font-size: var(--vscode-markdown-font-size, 14px);
	padding: 0 26px;
	line-height: var(--vscode-markdown-line-height, 22px);
	word-wrap: break-word;
}

#code-csp-warning {
	position: fixed;
	top: 0;
	right: 0;
	color: white;
	margin: 16px;
	text-align: center;
	font-size: 12px;
	font-family: sans-serif;
	background-color:#444444;
	cursor: pointer;
	padding: 6px;
	box-shadow: 1px 1px 1px rgba(0,0,0,.25);
}

#code-csp-warning:hover {
	text-decoration: none;
	background-color:#007acc;
	box-shadow: 2px 2px 2px rgba(0,0,0,.25);
}

body.scrollBeyondLastLine {
	margin-bottom: calc(100vh - 22px);
}

body.showEditorSelection .code-line {
	position: relative;
}

body.showEditorSelection .code-active-line:before,
body.showEditorSelection .code-line:hover:before {
	content: "";
	display: block;
	position: absolute;
	top: 0;
	left: -12px;
	height: 100%;
}

body.showEditorSelection li.code-active-line:before,
body.showEditorSelection li.code-line:hover:before {
	left: -30px;
}

.vscode-light.showEditorSelection .code-active-line:before {
	border-left: 3px solid rgba(0, 0, 0, 0.15);
}

.vscode-light.showEditorSelection .code-line:hover:before {
	border-left: 3px solid rgba(0, 0, 0, 0.40);
}

.vscode-light.showEditorSelection .code-line .code-line:hover:before {
	border-left: none;
}

.vscode-dark.showEditorSelection .code-active-line:before {
	border-left: 3px solid rgba(255, 255, 255, 0.4);
}

.vscode-dark.showEditorSelection .code-line:hover:before {
	border-left: 3px solid rgba(255, 255, 255, 0.60);
}

.vscode-dark.showEditorSelection .code-line .code-line:hover:before {
	border-left: none;
}

.vscode-high-contrast.showEditorSelection .code-active-line:before {
	border-left: 3px solid rgba(255, 160, 0, 0.7);
}

.vscode-high-contrast.showEditorSelection .code-line:hover:before {
	border-left: 3px solid rgba(255, 160, 0, 1);
}

.vscode-high-contrast.showEditorSelection .code-line .code-line:hover:before {
	border-left: none;
}

img {
	max-width: 100%;
	max-height: 100%;
}

a {
	text-decoration: none;
}

a:hover {
	text-decoration: underline;
}

a:focus,
input:focus,
select:focus,
textarea:focus {
	outline: 1px solid -webkit-focus-ring-color;
	outline-offset: -1px;
}

hr {
	border: 0;
	height: 2px;
	border-bottom: 2px solid;
}

h1 {
	padding-bottom: 0.3em;
	line-height: 1.2;
	border-bottom-width: 1px;
	border-bottom-style: solid;
}

h1, h2, h3 {
	font-weight: normal;
}

table {
	border-collapse: collapse;
}

table > thead > tr > th {
	text-align: left;
	border-bottom: 1px solid;
}

table > thead > tr > th,
table > thead > tr > td,
table > tbody > tr > th,
table > tbody > tr > td {
	padding: 5px 10px;
}

table > tbody > tr + tr > td {
	border-top: 1px solid;
}

blockquote {
	margin: 0 7px 0 5px;
	padding: 0 16px 0 10px;
	border-left-width: 5px;
	border-left-style: solid;
}

code {
	font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Courier New", monospace, "Droid Sans Fallback";
	font-size: 1em;
	line-height: 1.357em;
}

body.wordWrap pre {
	white-space: pre-wrap;
}

pre:not(.hljs),
pre.hljs code > div {
	padding: 16px;
	border-radius: 3px;
	overflow: auto;
}

pre code {
	color: var(--vscode-editor-foreground);
	tab-size: 4;
}

/** Theming */

.vscode-light pre {
	background-color: rgba(220, 220, 220, 0.4);
}

.vscode-dark pre {
	background-color: rgba(10, 10, 10, 0.4);
}

.vscode-high-contrast pre {
	background-color: rgb(0, 0, 0);
}

.vscode-high-contrast h1 {
	border-color: rgb(0, 0, 0);
}

.vscode-light table > thead > tr > th {
	border-color: rgba(0, 0, 0, 0.69);
}

.vscode-dark table > thead > tr > th {
	border-color: rgba(255, 255, 255, 0.69);
}

.vscode-light h1,
.vscode-light hr,
.vscode-light table > tbody > tr + tr > td {
	border-color: rgba(0, 0, 0, 0.18);
}

.vscode-dark h1,
.vscode-dark hr,
.vscode-dark table > tbody > tr + tr > td {
	border-color: rgba(255, 255, 255, 0.18);
}

</style>

<style>
/* a11y-dark theme */
/* Based on the Tomorrow Night Eighties theme: https://github.com/isagalaev/highlight.js/blob/master/src/styles/tomorrow-night-eighties.css */
/* @author: ericwbailey */

/* Comment */
.hljs-comment,
.hljs-quote {
  color: #d4d0ab;
}

/* Red */
.hljs-variable,
.hljs-template-variable,
.hljs-tag,
.hljs-name,
.hljs-selector-id,
.hljs-selector-class,
.hljs-regexp,
.hljs-deletion {
  color: #ffa07a;
}

/* Orange */
.hljs-number,
.hljs-built_in,
.hljs-builtin-name,
.hljs-literal,
.hljs-type,
.hljs-params,
.hljs-meta,
.hljs-link {
  color: #f5ab35;
}

/* Yellow */
.hljs-attribute {
  color: #ffd700;
}

/* Green */
.hljs-string,
.hljs-symbol,
.hljs-bullet,
.hljs-addition {
  color: #abe338;
}

/* Blue */
.hljs-title,
.hljs-section {
  color: #00e0e0;
}

/* Purple */
.hljs-keyword,
.hljs-selector-tag {
  color: #dcc6e0;
}

.hljs {
  display: block;
  overflow-x: auto;
  background: #2b2b2b;
  color: #f8f8f2;
  padding: 0.5em;
}

.hljs-emphasis {
  font-style: italic;
}

.hljs-strong {
  font-weight: bold;
}

@media screen and (-ms-high-contrast: active) {
  .hljs-addition,
  .hljs-attribute,
  .hljs-built_in,
  .hljs-builtin-name,
  .hljs-bullet,
  .hljs-comment,
  .hljs-link,
  .hljs-literal,
  .hljs-meta,
  .hljs-number,
  .hljs-params,
  .hljs-string,
  .hljs-symbol,
  .hljs-type,
  .hljs-quote {
        color: highlight;
    }

    .hljs-keyword,
    .hljs-selector-tag {
        font-weight: bold;
    }
}

</style>

<style>
/*
 * Markdown PDF CSS
 */

 body {
	font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif, "Meiryo";
	padding: 0 12px;
}

pre {
	background-color: #f8f8f8;
	border: 1px solid #cccccc;
	border-radius: 3px;
	overflow-x: auto;
	white-space: pre-wrap;
	overflow-wrap: break-word;
}

pre:not(.hljs) {
	padding: 23px;
	line-height: 19px;
}

blockquote {
	background: rgba(127, 127, 127, 0.1);
	border-color: rgba(0, 122, 204, 0.5);
}

.emoji {
	height: 1.4em;
}

code {
	font-size: 14px;
	line-height: 19px;
}

/* for inline code */
:not(pre):not(.hljs) > code {
	color: #C9AE75; /* Change the old color so it seems less like an error */
	font-size: inherit;
}

/* Page Break : use <div class="page"/> to insert page break
-------------------------------------------------------- */
.page {
	page-break-after: always;
}

</style>

<script src="https://unpkg.com/mermaid/dist/mermaid.min.js"></script>
</head>
<body>
  <script>
    mermaid.initialize({
      startOnLoad: true,
      theme: document.body.classList.contains('vscode-dark') || document.body.classList.contains('vscode-high-contrast')
          ? 'dark'
          : 'default'
    });
  </script>
<h1 id="seasky%E4%B8%B2%E5%8F%A3%E9%80%9A%E4%BF%A1%E5%8D%8F%E8%AE%AE">Seasky串口通信协议</h1>
<h3 id="%E5%8F%AF%E7%94%A8%E4%BA%8E%E8%A7%86%E8%A7%89%E4%BB%A5%E5%8F%8A%E5%85%B6%E4%BB%96%E8%87%AA%E7%A0%94%E6%A8%A1%E5%9D%97%E4%BB%85%E9%99%90%E4%B8%B2%E5%8F%A3%E9%80%9A%E4%BF%A1">可用于视觉，以及其他自研模块（仅限串口通信）</h3>
<h2 id="%E4%B8%80%E4%B8%B2%E5%8F%A3%E9%85%8D%E7%BD%AE">一、串口配置</h2>
<p>通信方式是串口，配置为波特率 115200，8 位数据位，1 位停止位，无硬件流控，无校验位。</p>
<h2 id="%E4%BA%8C%E6%8E%A5%E5%8F%A3%E5%8D%8F%E8%AE%AE%E8%AF%B4%E6%98%8E">二、接口协议说明</h2>
<ul>
<li>以下所有低位在前发送</li>
</ul>
<ol>
<li>
<p>通信协议格式</p>
<table>
<thead>
<tr>
<th>帧头</th>
<th>数据ID</th>
<th>数据</th>
<th>帧尾</th>
</tr>
</thead>
<tbody>
<tr>
<td>protocol_header(4-byte)</td>
<td>cmd_id(2-byte)</td>
<td>data (n-byte)</td>
<td>frame_tail(2-byte，CRC16，整包校验)</td>
</tr>
</tbody>
</table>
</li>
<li>
<p>帧头详细定义</p>
<table>
<thead>
<tr>
<th>帧头</th>
<th>偏移位置</th>
<th>字节大小</th>
<th>内容</th>
</tr>
</thead>
<tbody>
<tr>
<td>sof(CMD)</td>
<td>0</td>
<td>1</td>
<td>数据帧起始字节，固定值为 0xA5</td>
</tr>
<tr>
<td>data_length</td>
<td>1</td>
<td>2</td>
<td>数据帧中 data 的长度</td>
</tr>
<tr>
<td>crc_check</td>
<td>3</td>
<td>1</td>
<td>帧头CRC校验</td>
</tr>
</tbody>
</table>
</li>
<li>
<p>cmd_id 命令码 ID 说明(字节偏移 4，字节大小 2)</p>
<table>
<thead>
<tr>
<th>命令码</th>
<th>数据段长度</th>
<th>功能说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>0x0001(可修改)</td>
<td></td>
<td>BMI088姿态数据</td>
</tr>
<tr>
<td>0x0002</td>
<td></td>
<td>INA226数据</td>
</tr>
</tbody>
</table>
</li>
<li>
<p>数据段data (n-byte)</p>
<table>
<thead>
<tr>
<th>数据</th>
<th>偏移位置</th>
<th>字节大小</th>
<th>内容</th>
</tr>
</thead>
<tbody>
<tr>
<td>flags_register</td>
<td>6</td>
<td>2</td>
<td>16位标志置位寄存器</td>
</tr>
<tr>
<td>float_date（len）</td>
<td>8</td>
<td>4 * len</td>
<td>float数据内容（4 * len-byte）</td>
</tr>
</tbody>
</table>
</li>
<li>
<p>frame_tail(CRC16，整包校验)</p>
</li>
<li>
<p>使用待发送数据更新获取发送数据帧</p>
<pre><code> void get_protocol_send_data
 (uint16_t send_id,	 	   //信号id
 uint16_t flags_register, //16位寄存器
 float    *tx_data,		   //待发送的float数据
 uint8_t  float_length,   //float的数据长度
 uint8_t  *tx_buf,		     //待发送的数据帧
 uint16_t *tx_buf_len)	   //待发送的数据帧长度
</code></pre>
<p>使用说明，如定义需要发送的数据为 <code>flags_register_t</code>(寄存器，用于传递部分标志),<code>tx_data[2]</code> (假设只发视觉的两个数据x,y)，<code>tx_data_len = 2</code> (发送的float数据长度为2)，信号id定义为 <code>vis_id = 0x000A</code>;</p>
<p>定义发送数据发送缓冲区<code>uint8_t tx_buf[100]</code>,<code>uint16_t tx_buf_len = 0</code>
则上述函数调用后会更新 <code>tx_buf</code>中的数据，以及更新发送数据长度<code>tx_buf_len</code></p>
<pre><code> /*更新数据发送缓冲区*/
 get_protocol_send_data(vis_id,
                       &amp;flags_register_t,
                       tx_data,
                       &amp;tx_data_len,
                       tx_buf,
                       tx_buf_len);
 /*发送缓冲区数据，多字节发送函数，按字节形式发送，linux或win只需调用底层的字节发送接口函数*/
 usart6_send(tx_buf,tx_buf_len);
</code></pre>
</li>
<li>
<p>接收数据获取id，获取flags_register，rx_data</p>
<pre><code> uint16_t get_protocol_info
 (uint8_t  *rx_buf,			   //接收到的原始数据
  uint16_t *rx_pos,			   //原始数据指针
  uint16_t *flags_register, //接收数据的16位寄存器地址
  float    *rx_data)		     //接收的float数据存储地址
</code></pre>
<p>使用说明，首先将接收数据存储于 <code>rx_buf[]</code>,每接收一个数据 <code>*rx_pos</code>的值会加一，以便于存储下一个数据，原始数据缓冲的存储由中断方式完成，在linux或win上可能有所区别，调用此函数，函数中包含crc校验，数据解读等相关处理，最后会更新<code>flags_register</code>的值即为收到的16位寄存器的值，<code>rx_data[]</code>即为收到的float 数据。</p>
<p>值得注意的是如果改函数调用的返回值为非零时，才是获取到的正确ID，因此如果要获取正确ID需要加入判断，如下所示：</p>
<pre><code> void uart_task(void *pvParameters)
 {
   while(1)
   {
     uint16_t id_t = 0;
     id_t =  get_protocol_info
     (USART_RX_BUF,			//接收到的原始数据
     &amp;USART_RX_STA,			//原始数据指针
     &amp;ina226_t.ctr_t.flags,	//接收数据的16位寄存器地址
     ina226_t.ctr_t.rx_data);		//接收的float数据存储地址
     if(id_t!=0)ina226_t.ctr_t.ctr_id=id_t;
     vTaskDelay(10);
   }
 }
</code></pre>
</li>
<li>
<p>Seasky串口通信协议</p>
<blockquote>
<p>bsp_protocol.h</p>
</blockquote>
<pre><code> #ifndef _BSP_PROTOCOL_H
 #define _BSP_PROTOCOL_H

 #include &quot;main.h&quot;

 #define PROTOCOL_CMD_ID 0XA5

 #define OFFSET_BYTE 8	//出数据段外，其他部分所占字节数

 typedef struct
 {
     __packed struct
     {
         uint8_t  sof;
         uint16_t data_length;
         uint8_t  crc_check; //帧头CRC校验
     } header;					//数据帧头
     uint16_t cmd_id;	 		//数据ID
     uint16_t frame_tail; 		//帧尾CRC校验
 } protocol;

 /*更新发送数据帧，并计算发送数据帧长度*/
 void get_protocol_send_data
 (uint16_t send_id,	 	  //信号id
 uint16_t flags_register, //16位寄存器
 float    *tx_data,		  //待发送的float数据
 uint8_t  float_length,//float的数据长度
 uint8_t  *tx_buf,		  //待发送的数据帧
 uint16_t *tx_buf_len);	  //待发送的数据帧长度

 /*接收数据处理*/
 uint16_t get_protocol_info
 (uint8_t  *rx_buf,			//接收到的原始数据
 uint16_t *rx_pos,			//原始数据指针
 uint16_t *flags_register,	//接收数据的16位寄存器地址
 float    *rx_data);		//接收的float数据存储地址


 /*中断函数传值处理*/
 void PROTOCOL_RX_IRQ(uint8_t res,uint8_t *rx_buf,uint16_t *rx_buf_pos);
 #endif
</code></pre>
<blockquote>
<p>bsp_protocol.c</p>
</blockquote>
<pre><code> /*
   @SEASKY---2020/09/05
   Seasky串口通信协议
 */
 #include &quot;bsp_protocol.h&quot;

 #include &quot;bsp_crc8.h&quot;
 #include &quot;bsp_crc16.h&quot;

 /*获取CRC8校验码*/
 uint8_t Get_CRC8_Check(uint8_t *pchMessage,uint16_t dwLength)
 {
     return crc_8(pchMessage,dwLength);
 }
 /*检验CRC8数据段*/
 uint8_t CRC8_Check_Sum(uint8_t *pchMessage,uint16_t dwLength)
 {
     uint8_t ucExpected = 0;
     if ((pchMessage == 0) || (dwLength &lt;= 2)) return 0;
     ucExpected = Get_CRC8_Check(pchMessage, dwLength-1);
     return ( ucExpected == pchMessage[dwLength-1] );
 }
 /*获取CRC16校验码*/
 uint16_t Get_CRC16_Check(uint8_t *pchMessage,uint32_t dwLength)
 {
     return crc_16(pchMessage,dwLength);
 }
 /*检验CRC16数据段*/
 uint16_t CRC16_Check_Sum(uint8_t *pchMessage, uint32_t dwLength)
 {
     uint16_t  wExpected = 0;
     if ((pchMessage == 0) || (dwLength &lt;= 2))
         {
             return 0;
         }
     wExpected = Get_CRC16_Check ( pchMessage, dwLength - 2);
     return (((wExpected &amp; 0xff) == pchMessage[dwLength - 2] )&amp;&amp; (((wExpected &gt;&gt; 8) &amp; 0xff) == pchMessage[dwLength - 1]));
 }
 /*检验数据帧头*/
 uint8_t protocol_heade_Check(
     protocol *pro,
     uint8_t  *rx_buf,
     uint16_t *rx_pos)
 {
     if(rx_buf[0] == PROTOCOL_CMD_ID)
         {
             pro-&gt;header.sof = rx_buf[0];
             if(CRC8_Check_Sum(&amp;rx_buf[0],4))
                 {
                     pro-&gt;header.data_length = (rx_buf[2]&lt;&lt;8) | rx_buf[1];
                     pro-&gt;header.crc_check = rx_buf[3];
                     pro-&gt;cmd_id = (rx_buf[5]&lt;&lt;8) | rx_buf[4];
                     return  1;
                 }
         }
     else
         {
             *rx_pos = 0;
         }
     return 0;
 }

 float float_protocol(uint8_t *dat_t)
 {
     uint8_t f_data[4];
     f_data[0] = *(dat_t+0);
     f_data[1] = *(dat_t+1);
     f_data[2] = *(dat_t+2);
     f_data[3] = *(dat_t+3);
     return *(float*)f_data;
 }
 /*
   此函数根据待发送的数据更新数据帧格式以及内容，实现数据的打包操作
   后续调用通信接口的发送函数发送tx_buf中的对应数据
 */
 void get_protocol_send_data
 (uint16_t send_id,	 	  //信号id
 uint16_t flags_register, //16位寄存器
 float    *tx_data,		  //待发送的float数据
 uint8_t  float_length,//float的数据长度
 uint8_t  *tx_buf,		  //待发送的数据帧
 uint16_t *tx_buf_len)	  //待发送的数据帧长度
 {
     uint16_t crc16;
     uint16_t data_len;

     data_len = float_length*4+2;
     /*帧头部分*/
     tx_buf[0] = PROTOCOL_CMD_ID;
     tx_buf[1] = data_len &amp; 0xff;		      //低位在前
     tx_buf[2] = (data_len &gt;&gt; 8) &amp; 0xff;       //低位在前
     tx_buf[3] = Get_CRC8_Check(&amp;tx_buf[0],3); //获取CRC8校验位

     /*数据的信号id*/
     tx_buf[4] = send_id &amp; 0xff;
     tx_buf[5] = (send_id&gt;&gt;8) &amp; 0xff;

     /*建立16位寄存器*/
     tx_buf[6] = flags_register &amp; 0xff;
     tx_buf[7] = (flags_register&gt;&gt;8) &amp; 0xff;

     /*float数据段*/
     for(int i=0; i&lt;4*float_length; i++)
         {
             tx_buf[i+8]  = ((uint8_t*)(&amp;tx_data[i/4]))[i%4];
         }

     /*整包校验*/
     crc16 = Get_CRC16_Check(&amp;tx_buf[0],data_len+6);
     tx_buf[data_len+6] = crc16 &amp; 0xff;
     tx_buf[data_len+7] = (crc16 &gt;&gt;8) &amp; 0xff;

     *tx_buf_len = data_len+8;
 }
 /*
   此函数用于处理接收数据，
   返回数据内容的id
 */

 uint16_t get_protocol_info
 (uint8_t  *rx_buf,			//接收到的原始数据
 uint16_t *rx_pos,			//原始数据指针
 uint16_t *flags_register,	//接收数据的16位寄存器地址
 float    *rx_data)			//接收的float数据存储地址
 {
   static protocol pro;
   static uint16_t date_length;
     if(protocol_heade_Check(&amp;pro,rx_buf,rx_pos))
         {
             date_length = OFFSET_BYTE + pro.header.data_length;
             while(CRC16_Check_Sum(&amp;rx_buf[0],date_length))
                 {
                     *flags_register = (rx_buf[7]&lt;&lt;8) | rx_buf[6];
                     for(int i=0; i&lt;(pro.header.data_length-2)/4; i++)
                         {
                             rx_data[i] = float_protocol(&amp;rx_buf[8+4*i]);
                         }
                     for(int i=0; i&lt;date_length; i++)
                         {
                             rx_buf[i] = 0;
                         }
                     *rx_pos = 0;
                     return pro.cmd_id;
                 }
         }
     return 0;
 }
 /*放入中断接收函数*/
 void PROTOCOL_RX_IRQ(uint8_t res,uint8_t *rx_buf,uint16_t *rx_buf_pos)
 {
     rx_buf[*rx_buf_pos]=res;
     (*rx_buf_pos) +=1;
     if(rx_buf[0]!=PROTOCOL_CMD_ID)
         {
             *rx_buf_pos = 0;
         };
 }
</code></pre>
</li>
<li>
<p>通信协议依赖文件(CRC8,CRC16算法)</p>
<blockquote>
<p>bsp_crc16.h</p>
</blockquote>
<blockquote>
<p>bsp_crc16.c</p>
</blockquote>
<blockquote>
<p>bsp_crc8.h</p>
</blockquote>
<blockquote>
<p>bsp_crc8.c</p>
</blockquote>
</li>
</ol>

</body>
</html>
